﻿namespace com.crownpack.InitiativesDao
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Xml.Serialization;

    using com.crownpack.Extensions.SymbolExtensions;
    using com.crownpack.SharePoint;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Linq;

    public partial class Item : ICustomMapping, IItem
    {
        #region Fields

        public const string AUTHOR = "Created By";
        public const string CONTENTTYPEID = "ContentTypeId";
        public const string CREATED = "Created";
        public const string EDITOR = "Modified By";
        public const string MODIFIED = "Modified";
        public const string UNIQUEID = "UniqueId";

        const string ATTACHMENTS = "Attachments";
        const string ITEMEDITURL = "ItemEditUrl";
        const string ITEMVIEWURL = "ItemViewUrl";
        const string PARENTLISTID = "ParentLIstId";
        const string VERSIONLABEL = "VersionLabel";
        const string WEBURL = "WebUrl";

        List<SPFile> _Attachments;
        SPUserInfoExpanded _Author;
        DateTime _Created;
        SPUserInfoExpanded _Editor;
        string _internalAuthor;
        string _internalEditor;
        string _ItemEditUrl;
        string _ItemViewUrl;
        DateTime _Modified;
        Guid _ParentListId;
        Guid _UniqueID;
        string _VersionLabel;
        string _WebUrl;

        #endregion Fields

        #region Properties

        public List<SPFile> Attachments
        {
            get
            {
                if ( _Attachments == null )
                    _Attachments = new List<SPFile>( );
                return _Attachments;
            }
            set
            {
                const string property = ATTACHMENTS;
                if ( value != _Attachments )
                {
                    this.OnPropertyChanging( property, _Created );
                    _Attachments = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public SPUserInfoExpanded Author
        {
            get
            {
                return _Author == null ? new SPUserInfoExpanded( ) : _Author;
            }
            set
            {
                const string property = AUTHOR;
                this.OnPropertyChanging( property, _Author );
                _Author = value;
                this.OnPropertyChanged( property );
            }
        }

        public DateTime Created
        {
            get
            {
                return _Created;
            }
            set
            {
                const string property = CREATED;
                if ( value != _Created )
                {
                    this.OnPropertyChanging( property, _Created );
                    _Created = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public SPUserInfoExpanded Editor
        {
            get
            {
                return _Editor == null ? new SPUserInfoExpanded( ) : _Editor;
            }
            set
            {
                const string property = EDITOR;
                this.OnPropertyChanging( property, _Author );
                _Editor = value;
                this.OnPropertyChanged( property );
            }
        }

        [XmlIgnore]
        [Microsoft.SharePoint.Linq.ColumnAttribute( Name = "Author", Storage = "_internalAuthor", ReadOnly = true, FieldType = "User", IsLookupValue = true )]
        public string internalAuthor
        {
            get
            {
                return _internalAuthor;
            }
            set
            {
                using ( SPSite spSite = new SPSite( WebUrl ) )
                {
                    using ( SPWeb spWeb = spSite.OpenWeb( ) )
                    {
                        SPFieldUserValue o = new SPFieldUserValue( spWeb, value );
                        Author = new SPUserInfoExpanded( o.User.Name, o.User.LoginName, o.User.Email, o.User.ID, o.User.Sid, o.User.Notes );
                        _internalAuthor = value;
                    }
                }
            }
        }

        [XmlIgnore]
        [Microsoft.SharePoint.Linq.ColumnAttribute( Name = "Editor", Storage = "_internalEditor", ReadOnly = true, FieldType = "User", IsLookupValue = true )]
        public string internalEditor
        {
            get
            {
                return _internalEditor;
            }
            set
            {
                using ( SPSite spSite = new SPSite( WebUrl ) )
                {
                    using ( SPWeb spWeb = spSite.OpenWeb( ) )
                    {
                        SPFieldUserValue o = new SPFieldUserValue( spWeb, value );
                        Editor = new SPUserInfoExpanded( o.User.Name, o.User.LoginName, o.User.Email, o.User.ID, o.User.Sid, o.User.Notes );
                        _internalEditor = value;
                    }
                }
            }
        }

        public string ItemEditUrl
        {
            get
            {
                return _ItemEditUrl;
            }
            set
            {
                const string property = ITEMEDITURL;
                if ( value != _ItemEditUrl )
                {
                    this.OnPropertyChanging( property, _ItemEditUrl );
                    _ItemEditUrl = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public string ItemViewUrl
        {
            get
            {
                return _ItemViewUrl;
            }
            set
            {
                const string property = ITEMVIEWURL;
                if ( value != _ItemViewUrl )
                {
                    this.OnPropertyChanging( property, _ItemViewUrl );
                    _ItemViewUrl = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        [XmlIgnore]
        public SPListItem ListItem
        {
            get
            {
                SPListItem spListItem = null;
                using ( SPSite spSite = new SPSite( WebUrl ) )
                {
                    using ( SPWeb spWeb = spSite.OpenWeb( ) )
                    {
                        SPList spList = spWeb.Lists[ ParentListId ];
                        spListItem = spList.GetItemByUniqueId( UniqueId );
                    }
                }
                return spListItem;
            }
        }

        public DateTime Modified
        {
            get
            {
                return _Modified;
            }
            set
            {
                const string property = MODIFIED;
                if ( value != _Modified )
                {
                    this.OnPropertyChanging( property, _Modified );
                    _Modified = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public Guid ParentListId
        {
            get
            {
                return _ParentListId;
            }
            set
            {
                const string property = PARENTLISTID;
                if ( value != _ParentListId )
                {
                    this.OnPropertyChanging( property, _ParentListId );
                    _ParentListId = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public Guid UniqueId
        {
            get
            {
                return _UniqueID;
            }
            set
            {
                const string property = UNIQUEID;
                if ( value != _UniqueID )
                {
                    this.OnPropertyChanging( property, _UniqueID );
                    _UniqueID = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public string VersionLabel
        {
            get
            {
                return _VersionLabel;
            }
            set
            {
                const string property = VERSIONLABEL;
                if ( value != _VersionLabel )
                {
                    this.OnPropertyChanging( property, _VersionLabel );
                    _VersionLabel = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        public string WebUrl
        {
            get
            {
                return _WebUrl;
            }
            set
            {
                const string property = WEBURL;
                if ( value != _WebUrl )
                {
                    this.OnPropertyChanging( property, _WebUrl );
                    _WebUrl = value;
                    this.OnPropertyChanged( property );
                }
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Returns the internal name of the list field or column.
        /// Example: 
        /// CLASS c = null;
        /// string s = Item.GetColumnName&lt;CLASS&gt;( c.GetPropertySymbol( o=> o.PROPERTY ) );
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string GetColumnName<T>( string name )
        {
            object[ ] o = typeof( T ).GetProperty( name ).GetCustomAttributes( typeof( ColumnAttribute ), false );
            if ( o.Length > 0 )
                return ( ( ColumnAttribute ) o[ 0 ] ).Name;
            else
                return string.Empty;
        }

        public static string GetContentTypeId<T>( )
        {
            MemberInfo info = typeof( T );
            object[ ] o = info.GetCustomAttributes( typeof( ContentTypeAttribute ), false );
            if ( o.Length > 0 )
                return ( ( ContentTypeAttribute ) o[ 0 ] ).Id;
            else
                return string.Empty;
        }

        public static string GetContentTypeName<T>( )
        {
            MemberInfo info = typeof( T );
            object[ ] o = info.GetCustomAttributes( typeof( ContentTypeAttribute ), false );
            if ( o.Length > 0 )
                return ( ( ContentTypeAttribute ) o[ 0 ] ).Name;
            else
                return string.Empty;
        }

        public void MapFrom( object listItem )
        {
            SPListItem item = ( SPListItem ) listItem;

            foreach ( SPFolder f in item.ParentList.RootFolder.SubFolders[ "Attachments" ].SubFolders )
            {
                if ( f.Name.Equals( item.ID.ToString( ) ) )
                {
                    Attachments = f.Files.Cast<SPFile>( ).ToList( );
                    break;
                }
            }

            ParentListId = item.ParentList.ID;

            this.WebUrl = item.Web.Url;

            SPList spList = item.ParentList;

            string displayUrl = spList.Forms[ PAGETYPE.PAGE_DISPLAYFORM ].Url;
            this.ItemViewUrl = string.Format( "{0}/{1}?ID={2}, View Source", WebUrl, displayUrl, item.ID );

            string editUrl = spList.Forms[ PAGETYPE.PAGE_EDITFORM ].Url;
            this.ItemEditUrl = string.Format( "{0}/{1}?ID={2}, Edit Source", WebUrl, editUrl, item.ID );

            this.UniqueId = new Guid( Convert.ToString( item[ UNIQUEID ] ) );

            this.internalAuthor = item[ AUTHOR ].ToString( );
            this.internalEditor = item[ EDITOR ].ToString( );
            this.Created = DateTime.Parse( item[ CREATED ].ToString( ) );
            this.Modified = DateTime.Parse( item[ MODIFIED ].ToString( ) );
        }

        /// <summary>
        /// This method does not need to be implemented because SharePoint will write information to the Created, Author, Modified, ModifiedBy, UniqueId fields.
        /// </summary>
        /// <param name="listItem"></param>
        public void MapTo( object listItem )
        {
        }

        public void Resolve( RefreshMode mode, object originalListItem, object databaseListItem )
        {
            SPListItem originalItem = ( SPListItem ) originalListItem;
            SPListItem databaseItem = ( SPListItem ) databaseListItem;

            SPFieldUserValue originalAuthorValue = ( SPFieldUserValue ) originalItem[ AUTHOR ];
            SPFieldUserValue dbAuthorValue = ( SPFieldUserValue ) databaseItem[ AUTHOR ];

            SPFieldUserValue originalEditorValue = ( SPFieldUserValue ) originalItem[ EDITOR ];
            SPFieldUserValue dbEditorValue = ( SPFieldUserValue ) databaseItem[ EDITOR ];

            DateTime originalCreatedValue = ( DateTime ) originalItem[ CREATED ];
            DateTime dbCreatedValue = ( DateTime ) databaseItem[ CREATED ];

            DateTime originalModifiedValue = ( DateTime ) originalItem[ MODIFIED ];
            DateTime dbModifiedValue = ( DateTime ) databaseItem[ MODIFIED ];

            Guid originalUniqueIdValue = ( Guid ) originalItem[ UNIQUEID ];
            Guid dbUniqueIdValue = ( Guid ) databaseItem[ UNIQUEID ];

            //SPAttachmentCollection originalContentAttachmentsValue = originalItem[ CONTENTATTACHMENTS ] as SPAttachmentCollection;
            //SPAttachmentCollection dbContentAttachmentsValue = originalItem[ CONTENTATTACHMENTS ] as SPAttachmentCollection;

            if ( mode == RefreshMode.OverwriteCurrentValues )
            {
                this.internalAuthor = dbAuthorValue.ToString( );
                this.Created = dbCreatedValue;
                this.Modified = dbModifiedValue;
                this.internalEditor = dbEditorValue.ToString( );
                this.UniqueId = dbUniqueIdValue;
                //this.ContentAttachments = dbContentAttachmentsValue;
            }
            else if ( mode == RefreshMode.KeepCurrentValues )
            {
                databaseItem[ AUTHOR ] = this.internalAuthor;
                databaseItem[ CREATED ] = this.Created;
                databaseItem[ MODIFIED ] = this.Modified;
                databaseItem[ EDITOR ] = this.Editor;
                databaseItem[ UNIQUEID ] = this.UniqueId;
                //databaseItem[ CONTENTATTACHMENTS ] = this.ContentAttachments;
            }
            else if ( mode == RefreshMode.KeepChanges )
            {
                if ( this.internalAuthor != originalAuthorValue.User.Name )
                    databaseItem[ AUTHOR ] = this.internalAuthor;
                else if ( this.internalAuthor == originalAuthorValue.User.Name && this.internalAuthor != dbAuthorValue.User.Name )
                    this.internalAuthor = dbAuthorValue.ToString( );

                if ( this.internalEditor != originalEditorValue.User.Name )
                    databaseItem[ EDITOR ] = this.Editor;
                else if ( this.internalEditor == originalEditorValue.User.Name && this.internalEditor != dbEditorValue.User.Name )
                    this.internalEditor = dbEditorValue.ToString( );

                if ( this.Created != originalCreatedValue )
                    databaseItem[ CREATED ] = this.Created;
                else if ( this.Created == originalCreatedValue && this.Created != dbCreatedValue )
                    this.Created = dbCreatedValue;

                if ( this.Modified != originalModifiedValue )
                    databaseItem[ MODIFIED ] = this.Modified;
                else if ( this.Modified == originalModifiedValue && this.Modified != dbModifiedValue )
                    this.Modified = dbModifiedValue;

                if ( this.UniqueId != originalUniqueIdValue )
                    databaseItem[ UNIQUEID ] = this.UniqueId;
                else if ( this.UniqueId == originalUniqueIdValue && this.UniqueId != dbUniqueIdValue )
                    this.UniqueId = dbUniqueIdValue;

                //if ( this.ContentAttachments != originalContentAttachmentsValue )
                //    databaseItem[ CONTENTATTACHMENTS ] = this.ContentAttachments;
                //else if ( this.ContentAttachments == originalContentAttachmentsValue && this.ContentAttachments != dbContentAttachmentsValue )
                //    this.ContentAttachments = dbContentAttachmentsValue;
            }
        }

        #endregion Methods
    }
}